#include "stdafx.h"
#include "cmd.h"
#include <windows.h>
#include <list>
#include <QEnvPath.h>

/* Returns a list of directories (except the ones that begin with a dot) */

void GetDirectories(std::vector<std::string> &out, const std::string &directory, const std::string &ext, bool recurs)
{
#if defined(WIN32) || defined(_W64)
    HANDLE dir;
    WIN32_FIND_DATA file_data;

    if ((dir = FindFirstFileA((directory + "/*").c_str(), &file_data)) == INVALID_HANDLE_VALUE)
    	return; /* No files found */

    do {
    	const std::string file_name = file_data.cFileName;
    	const std::string full_file_name = directory + "/" + file_name;
    	const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
    	const bool is_file = (file_data.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) != 0;

    	if (file_name[0] == '.')
    		continue;
		
		if( is_directory && recurs ) GetDirectories( out, full_file_name, ext, recurs );
		if( !ext.empty() )
		{
			if( QEnvPath(file_name).extension() == ext )
    			out.push_back(full_file_name);
		}
		else out.push_back(full_file_name);
    } while (FindNextFile(dir, &file_data));

    FindClose(dir);
#else
    DIR *dir;
    class dirent *ent;
    class stat st;

    dir = opendir(directory);
    while ((ent = readdir(dir)) != NULL) {		
    	const std::string file_name = ent->d_name;
    	const std::string full_file_name = directory + "/" + file_name;
    	if (file_name[0] == '.')
    		continue;

    	if (stat(full_file_name.c_str(), &st) == -1)
    		continue;

    	const bool is_directory = (st.st_mode & S_IFDIR) != 0;

    	if (!is_directory)
    		continue;

    	out.push_back(full_file_name);
    }
    closedir(dir);
#endif
} // GetFilesInDirectory
std::vector<std::string> projects;
std::string homePath;
std::string versionFile;
Lit::Sdk::Version version;
#ifndef NDEBUG
bool verbose = true;
#else
bool verbose = false;
#endif
bool strStartsWith( const std::string &str, const std::string &search, bool lower )
{
	auto j = search.begin();
	for( auto i=str.begin(); (j!=search.end() && i!=str.end()); i++ )
	{
		if( lower )
		{
			char la = tolower( *i );
			char lb = tolower( *j );
			if( la != lb ) return false;
		}
		else
		{
			if( *i != *j ) return false;
		}
		j++;
	}
	return true;
}
bool processCommand( const std::string &command )
{
	std::stringstream ss(command);
	std::string c, word;
	ss >> c;
	if( verbose ) std::cout << "Processing: " << command << std::endl;
	if( c == "home" )
	{
		homePath = "";
		while( ss >> word ) homePath += safePath(word)+ " ";
		homePath = safePath(homePath);
		
	}
	else if( c == "verbose" )
	{
		std::cout << "Verbose Enabled" << std::endl;
		verbose = true;
	}
	else if( c == "project" )
	{
		std::string n;
		while( ss >> word ) n += word + " ";
		projects.push_back( n );
		if( verbose ) std::cout << "Adding Project '"+word+"'" << std::endl;
	}
	else 
		return false;

	return true;
}
void StringExplode(std::string str, std::string separator, std::vector<std::string>* results){
    int found;
    found = str.find_first_of(separator);
    while(found != std::string::npos){
        if(found > 0){
            results->push_back(str.substr(0,found));
        }
        str = str.substr(found+separator.size());
        found = str.find(separator);
    }
    if(str.length() > 0){
        results->push_back(str);
    }
}
int treatCommands( const std::string& args )
{
	if( args.empty() ) return CMD_EMPTY;
	
	std::cout << "Command Line: " << args << std::endl;
	std::vector<std::string> ex;
	StringExplode( args, "--", &ex );
	for( std::vector<std::string>::iterator i=ex.begin(); i!=ex.end(); i++ )
		if( !processCommand( *i ) ) return CMD_UNKNOWN;

	return CMD_SUCCESS;
}

std::string usage( )
{
	std::stringstream ss;
	ss << "Usage:" << std::endl;
	ss << "\t--project			| Adds a projects from the sdk dir" << std::endl;
	ss << "\t--home				| Sets the sdk home" << std::endl;
	ss << "\t--verbose			| Enables verbose mode, blah" << std::endl;
	return ss.str();
}

std::string trim( const std::string &str )
{
	std::string ret = str;
	while( !ret.empty() && ret.at(0) == ' ' ) ret.erase( ret.begin() );
	while( !ret.empty() && ret.at(ret.size()-1) == ' ' ) ret.erase( ret.begin()+(ret.size()-1) );
	return ret;
}
bool file_exists(const std::string& filename)
{
    if (FILE * file = fopen(filename.c_str(), "r"))
    {
        fclose(file);
        return true;
    }
    return false;
}

std::string safePath( const std::string &p )
{
	std::string ret = p;
	size_t pos = 0;
	while( (pos=ret.find('\\')) != std::string::npos )
	{
		ret[pos] = '/';
	}
	
	pos = 0;
	while( (pos=ret.find("//")) != std::string::npos )
	{
		ret.erase(ret.begin()+pos);
	}
	ret = trim(ret);
	if( !ret.empty() && ret.at(ret.size()-1) == '/' ) ret.erase(ret.begin()+(ret.size()-1));
	return ret;
}
Lit::Sdk::Version findCoreVersion( )
{
	Lit::Sdk::Version v;
	std::ifstream ifs( versionFile.c_str(), std::ios::in );
	if( !ifs.good() ) return v;
	while( !ifs.eof() )
	{
		std::string ln;
		std::getline( ifs, ln );

		std::stringstream ss(ln);
		std::string word;
		while( ss >> word )
		{
			if( word == "#define" )
			{
				ss >> word;
				if( word == "PROJECT_VERSION_MAJOR" ){ ss >> v.Major; if( verbose ) std::cout << "\tFound Version Major: " << v.Major << std::endl; }
				else if( word == "PROJECT_VERSION_MINOR" ){ ss >> v.Minor; if( verbose ) std::cout << "\tFound Version Minor: " << v.Minor << std::endl; }
				else if( word == "PROJECT_VERSION_BUILD" ){ ss >> v.Build; if( verbose ) std::cout << "\tFound Version Build: " << v.Build << std::endl; }
				else if( word == "PROJECT_VERSION_REVISION" ){ ss >> v.Rev; if( verbose ) std::cout << "\tFound Version Revision: " << v.Rev << std::endl; }
			}
		}
		
	}
	ifs.close();
	return v;
}
std::string findCoreVersionFile( )
{
	if( verbose ) std::cout << "Finding version file ..." << std::endl;
	std::string ret = safePath(homePath+"/ProjectVersion.h");
	if( verbose ) std::cout << "\tChecking " << ret << std::endl;
	bool exists = file_exists( ret );
	if( verbose && exists ) std::cout << "\t+ found at " << ret << std::endl;
	if( exists ) return ret;
	
	ret = safePath(homePath+"/CoreLibrary/ProjectVersion.h");
	if( verbose ) std::cout << "\tChecking " << ret << std::endl;
	exists = file_exists( ret );
	if( verbose && exists ) std::cout << "\t+ found at " << ret << std::endl;
	if( exists ) return ret;

	ret = safePath(homePath+"/CoreLibrary/include/ProjectVersion.h");
	if( verbose ) std::cout << "\tChecking " << ret << std::endl;
	exists = file_exists( ret );
	if( verbose && exists ) std::cout << "\t+ found at " << ret << std::endl;
	if( exists ) return ret;

	ret = safePath(homePath+"/Sdk/CoreLibrary/ProjectVersion.h");
	if( verbose ) std::cout << "\tChecking " << ret << std::endl;
	exists = file_exists( ret );
	if( verbose && exists ) std::cout << "\t+ found at " << ret << std::endl;
	if( exists ) return ret;

	ret = safePath(homePath+"/Sdk/CoreLibrary/include/ProjectVersion.h");
	if( verbose ) std::cout << "\tChecking " << ret << std::endl;
	exists = file_exists( ret );
	if( verbose && exists ) std::cout << "\t+ found at " << ret << std::endl;
	if( exists ) return ret;

	return ret;
}
std::string make_path( const std::string & drive, const std::string &dir, const std::string &name, const std::string &ext )
{
	char path_buffer[4096];
	_makepath_s<4096>( path_buffer, drive.c_str(), dir.c_str(), name.c_str(), ext.c_str() ); // C4996
	return std::string(path_buffer);
}
bool split_path( const std::string & path, std::string &drive, std::string &dir, std::string &name, std::string &ext )
{
	char _path_buffer[_MAX_PATH];
	char _drive[_MAX_DRIVE];
	char _dir[_MAX_DIR];
	char _fname[_MAX_FNAME];
	char _ext[_MAX_EXT];
	errno_t err;
	
	err = _splitpath_s( &path.at(0), _drive, _MAX_DRIVE, _dir, _MAX_DIR, _fname,
                       _MAX_FNAME, _ext, _MAX_EXT );
	if (err != 0)
	{
		printf("Error splitting the path. Error code %d.\n", err);
		return false;
	}
	
	drive = _drive;
	dir = _dir;
	name = _fname;
	ext = _ext;
	return true;
}

void init_report()
{
	system("cls");
	std::cout << "Verbose: " << std::string(verbose?"enabled":"disabled") << std::endl;
	std::cout << "SDK Path: " << homePath << std::endl;
	std::cout << "SDK Version Info: ";
	versionFile = findCoreVersionFile();
	version = findCoreVersion();
	std::cout << "SDK Version: " << version << std::endl;
}
void createProjectMakefile( const std::string &name )
{
	std::string base = safePath( homePath+"/snapshots/"+std::string(version)+"/"+name );
	CreateDirectory(base.c_str(), NULL );
	if( verbose ) std::cout << "Cerating snapshot project directory: " << base << std::endl;
	std::vector<std::string> v;
	GetDirectories( v, homePath+"/Sdk/"+name, "", true );
		
	std::ofstream os( homePath+"/snapshots/"+std::string(version)+"/"+name+".makefile", std::ios::out );
	if(verbose)std::cout<<"\t+ Creating stream: " << homePath+"/snapshots/"+std::string(version)+"/"+name+".makefile" << std::endl;

	if(os.good())
	{
		for( auto i=v.begin(); i!=v.end(); i++ )
		{
			os << *i << std::endl;
		}
		os.close();
	}
}
std::string basename( const std::string str )
{
	std::string ret=safePath(str);
	size_t p = str.find_last_of('/');
	if( p == std::string::npos ) return ret;
	return ret.substr( p+1, ret.size()-(p+1) );
}
void init( )
{
	init_report();
	
	CreateDirectory( safePath( homePath+"/snapshots/").c_str(), NULL );
	CreateDirectory( safePath( homePath+"/snapshots/"+std::string(version)+"/").c_str(), NULL );
	if( verbose ) std::cout << "Creating snapshot directory: " << safePath( homePath+"/snapshots/"+std::string(version)+"/") << std::endl;
	
	std::vector<std::string> vp;
	for( auto i=projects.begin(); i!=projects.end(); i++ )
	{
		*i = trim(*i);
		if( !i->empty() )vp.push_back(*i);
	}
	projects = vp;

	for( auto i=projects.begin(); i!=projects.end(); i++ )
		createProjectMakefile( *i );
	
	for( auto i=projects.begin(); i!=projects.end(); i++ )
	{
		std::ifstream f( safePath( homePath+"/snapshots/"+std::string(version)+"/"+*i+".makefile" ), std::ios::in );
		if(!f.good()) std::cout << "Cannot make snapshot of " << *i << std::endl;
		else
		{
			std::string line;
			while( !f.eof() )
			{
				std::string p = safePath( homePath+"/snapshots/"+std::string(version)+"/"+*i+"/" );
				std::getline( f, line );
				if( !line.empty() )
				{
					
					if( basename(line).find('.') == std::string::npos )
					{
						CreateDirectory( (p+"/"+basename(line)).c_str(), NULL );
					}
					else
					{
						if( CopyFile( line.c_str(), (p+"/"+basename(line)).c_str(), FALSE ) == FALSE )
							std::cout << "Couldn't copy " << line << " to " << p+"/"+basename(line) << std::endl;
						else if( verbose )
							std::cout << "Successfully copied " << line << " to " << p+"/"+basename(line) << std::endl;
					}
				}
			}
			f.close();
		}
	}
}